package engine.systems.update
{
	import ash.core.Engine;
	import ash.core.Entity;
	import ash.core.NodeList;
	import ash.core.System;
	
	import engine.GameEngine;
	import engine.classes.ToggleKey;
	import engine.classes.enumerations.SystemPriority;
	import engine.managers.ScreenManager;
	import engine.systems.render.camera.CameraNode;
	import engine.utils.EntityUtils;
	
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.system.System;
	import flash.text.StyleSheet;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.ui.Keyboard;
	import flash.utils.getTimer;
	
	public class GameMonitorSystem extends ash.core.System
	{
		//Monitor text variables
		private var xml:XML;
		private var style:StyleSheet;
		private var text:TextField;
		
		//Used for updates.
		private var toggleKey:ToggleKey;
		private var active:Boolean = false;
		private var time:Number = 0;
		public var waitTime:Number = 0.1;
		
		//FPS variables
		private var oldTime:Number = getTimer();
		private var fps:Number = 0;
		private var frameRates:Vector.<Number> = new Vector.<Number>();
		private var ms:int = 0;
		
		//Entity variables
		private var entities:Vector.<Entity>;
		private var numAwake:int;
		private var numAsleep:int;
		
		//Stage variables
		private var cameras:NodeList;
		private const stage:Stage = GameEngine.screenManager.stage;
		private var children:int;
		
		private var systemManager:Engine;
		
		public function GameMonitorSystem()
		{
			this.setupXML();
			this.setupStyleSheet();
			this.setupTextField();
			
			this.toggleKey = new ToggleKey(Keyboard.Z);
			this.toggleKey.pressed.add(this.setText);
			
			this.priority = SystemPriority.UPDATE;
		}
		
		override public function update(time:Number):void
		{
			this.toggleKey.update();
			
			if(!this.text.visible) return;
			
			//Can set a delay
			this.time += time;
			if(this.time < this.waitTime) return;
			this.time = 0;
			
			//Memory Monitor (Sytem memory is in bytes, so dividing by 1048576 converts it to MBs.)
			this.xml.privateMemory 	= (flash.system.System.privateMemory / 1048576).toFixed(3);
			this.xml.totalMemory 	= (flash.system.System.totalMemory 	 / 1048576).toFixed(3);
			this.xml.garbage 		= (flash.system.System.freeMemory  	 / 1048576).toFixed(3);
			
			//Entity Monitor
			this.entities 		= this.systemManager.entities;
			this.numAwake 		= 0;
			this.numAsleep 		= 0;
			
			for each(var entity:Entity in entities)
				EntityUtils.sleeping(entity) ? this.numAsleep++ : this.numAwake++;
			
			this.xml.entities 	= this.entities.length;
			this.xml.awake 		= this.numAwake;
			this.xml.asleep 	= this.numAsleep;
			
			//Stage Monitor
			var camera:CameraNode = this.cameras.head;
			if(camera)
			{
				this.xml.width 	= Math.round(camera.camera.viewport.width) 	+ " / " + this.stage.stageWidth;
				this.xml.height = Math.round(camera.camera.viewport.height) + " / " + this.stage.stageHeight;
			}
			
			this.children 		= 0;
			this.countChildren(this.stage);
			this.xml.children 	= this.children;
			
			//Set the text to what the XML's been set to.
			this.text.htmlText 	= this.xml;
		}
		
		private function setText():void
		{
			this.text.visible = !this.text.visible;
		}
		
		private function enterFrame(event:Event):void
		{
			if(!this.text.visible) return;
			
			var newTime:int = getTimer();
			if(newTime - 1000 > this.oldTime)
			{
				var length:int = this.frameRates.push(this.fps);
				if(length > 60) this.frameRates.shift();
				
				var average:Number = 0;
				for each(var frameRate:Number in this.frameRates) average += frameRate;
				average = average / this.frameRates.length;
				
				this.xml.avg = Math.round(average);
				this.xml.fps = this.fps + " / " + this.stage.frameRate;
				
				this.fps = 0;
				this.oldTime = newTime;
			}
			this.fps++;
			
			this.xml.ms = newTime - this.ms;
			this.ms = newTime;
		}
		
		override public function addToEngine(systemManager:Engine):void
		{
			this.systemManager = systemManager;
			
			this.cameras = systemManager.getNodeList(CameraNode);
			
			this.text.addEventListener(Event.ENTER_FRAME, this.enterFrame);
			GameEngine.screenManager.debugContainer.addChild(this.text);
		}
		
		override public function removeFromEngine(systemManager:Engine):void
		{
			this.systemManager = null;
			
			this.cameras = null;
			
			this.text.removeEventListener(Event.ENTER_FRAME, this.enterFrame);
			GameEngine.screenManager.debugContainer.removeChild(this.text);
		}
		
		private function countChildren(container:DisplayObjectContainer):void
		{
			this.children += container.numChildren;
			
			for(var i:int = 0; i < container.numChildren; i++)
			{
				var child:DisplayObject = container.getChildAt(i);
				if(child is DisplayObjectContainer)
					this.countChildren(child as DisplayObjectContainer);
			}
		}
		
		private function setupXML():void
		{
			this.xml =
			<xml>
				<title>FPS MONITOR</title>

					<subtitle>FPS: </subtitle>
						<fps>-</fps>

					<subtitle>Average: </subtitle>
						<avg>-</avg>

					<subtitle>Milliseconds: </subtitle>
						<ms>-</ms>

				<title>MEMORY MONITOR</title>

					<subtitle>Private: </subtitle>
						<privateMemory>-</privateMemory>
				
					<subtitle>Total: </subtitle>
						<totalMemory>-</totalMemory>

					<subtitle>Garbage: </subtitle>
						<garbage>-</garbage>
				
				<title>ENTITY MONITOR</title>

					<subtitle>Entities: </subtitle>
						<entities>-</entities>
				
					<subtitle>Awake: </subtitle>
						<awake>-</awake>

					<subtitle>Asleep: </subtitle>
						<asleep>-</asleep>

				<title>STAGE MONITOR</title>

					<subtitle>Width: </subtitle>
						<width>-</width>

					<subtitle>Height: </subtitle>
						<height>-</height>

					<subtitle>Children: </subtitle>
						<children>-</children>
			</xml>;
		}
		
		private function setupStyleSheet():void
		{
			this.style = new StyleSheet();
			
			//General
			this.style.setStyle("xml", 		{fontSize:"12px", fontFamily:"arial"});
			this.style.setStyle("title", 	{color:"#FF3300"});
			this.style.setStyle("subtitle", {color:"#CCCCCC", display:"inline"});
			
			//FPS
			this.style.setStyle("fps", 	{color:"#FFFFFF"});
			this.style.setStyle("avg", 	{color:"#FFFFFF"});
			this.style.setStyle("ms", 	{color:"#FFFFFF"});
			
			//Memory
			this.style.setStyle("totalMemory", 		{color:"#FFFFFF"});
			this.style.setStyle("privateMemory", 	{color:"#FFFFFF"});
			this.style.setStyle("garbage", 			{color:"#FFFFFF"});
			
			//Entities
			this.style.setStyle("entities", {color:"#FFFFFF"});
			this.style.setStyle("awake", 	{color:"#FFFFFF"});
			this.style.setStyle("asleep", 	{color:"#FFFFFF"});
			
			//Stage
			this.style.setStyle("width", 	{color:"#FFFFFF"});
			this.style.setStyle("height", 	{color:"#FFFFFF"});
			this.style.setStyle("children", {color:"#FFFFFF"});
		}
		
		private function setupTextField():void
		{
			this.text 					= new TextField();
			this.text.alpha 			= 0.8;
			this.text.autoSize 			= TextFieldAutoSize.LEFT;
			this.text.styleSheet 		= this.style;
			this.text.condenseWhite 	= true;
			this.text.selectable		= false;
			this.text.mouseEnabled		= false;
			this.text.background		= true;
			this.text.backgroundColor	= 0x000000;
			this.text.visible 			= false;
		}
	}
}